import { Shader } from "@nirvana/shaders";
import { Vector3 } from "@nirvana/math";
import { WebGLCanvas } from "@nirvana/canvas";
import {
  Data,
  Mesh,
  DataElement,
  Geometry,
  ShaderMaterial,
  Camera,
  AmbientLight,
  DirectionalLight,
  PointLight,
  TextureUtil,
  SceneNode,
  WireframeMesh
} from "@nirvana/core";
import * as Common from "@loaders/commons";
import { OBJLoader } from "@loaders/obj";
import * as dat from "dat.gui";
let Options = function () {
  // this.message = "dat.gui";
  // this.speed = 0.8;
  // this.displayOutline = false;
  // this.button = function () {};
  this.translateX = 0;
  this.translateY = 0;
  this.translateZ = 0;

  this.scale = 1.0;
  this.scaleX = 1.0;
  this.scaleY = 1.0;
  this.scaleZ = 1.0;
  this.rotate = 0;
};

async function main() {
  var options = new Options();
  var gui = new dat.GUI();

  // 使用使用 Canvas
  let canvas = document.getElementById("engine-nirvana") as HTMLCanvasElement;
  const webCanvas = new WebGLCanvas(canvas, {});
  const engine = webCanvas.engine;
  const scene = engine.sceneManager.createScene("root");
  scene.addLight(new AmbientLight(""));
  scene.addLight(new DirectionalLight(""));
  scene.addLight(new PointLight(""));
  scene.addLight(new PointLight(""));
  const camera = new Camera("root camera", engine);
  scene.addCamera(camera);
  camera.transform.setPosition(0, 0, 10);
  const rootEntity = new SceneNode("");
  scene.addEntity(rootEntity);
  // rootEntity.transform.scale(new Vector3(0.3, 0.3, 0.3));

  const gl = engine.gl;

  const cVS = `
  attribute vec4 a_position;
  attribute vec3 a_normal;
  attribute vec2 a_texcoord;
  // attribute vec4 a_color;

  uniform mat4 u_projMat;
  uniform mat4 u_viewMat;
  uniform mat4 u_modelMat;
  uniform mat4 u_normalMat;
  uniform vec3 u_viewWorldPosition;

  varying vec3 v_normal;
  varying vec3 v_surfaceToView;
  varying vec2 v_texcoord;
  varying vec4 v_color;

  void main() {
    gl_Position = u_projMat * u_viewMat * u_modelMat * a_position;
    v_surfaceToView = u_viewWorldPosition - a_position.xyz;
    v_normal = mat3(u_normalMat) * a_normal;
    v_texcoord = a_texcoord;
    v_color = vec4(1.0,1.0,1.0,1.0);
  }
  `;
  const cFS = `
  precision mediump float;

  uniform vec3 diffuse;
  uniform sampler2D diffuseMap;
  uniform vec3 ambient;
  uniform vec3 emissive;
  uniform vec3 specular;
  uniform sampler2D specularMap;
  uniform float shininess;
  uniform float opacity;
  uniform vec3 u_lightDirection;
  uniform vec3 u_ambientLight;

  varying vec3 v_normal;
  varying vec3 v_surfaceToView;
  varying vec2 v_texcoord;
  varying vec4 v_color;

  void main () {
    vec3 normal = normalize(v_normal);

    vec3 surfaceToViewDirection = normalize(v_surfaceToView);
    vec3 halfVector = normalize(u_lightDirection + surfaceToViewDirection);

    float fakeLight = dot(u_lightDirection, normal) * .5 + .5;
    float specularLight = clamp(dot(normal, halfVector), 0.0, 1.0);
    vec4 specularMapColor = texture2D(specularMap, v_texcoord);
    vec3 effectiveSpecular = specular * specularMapColor.rgb;

    vec4 diffuseMapColor = texture2D(diffuseMap, v_texcoord);
    vec3 effectiveDiffuse = diffuse * diffuseMapColor.rgb * v_color.rgb;
    float effectiveOpacity = opacity * diffuseMapColor.a * v_color.a;

    gl_FragColor = vec4(
        emissive +
        ambient * u_ambientLight +
        effectiveDiffuse * fakeLight +
        effectiveSpecular * pow(specularLight, shininess),
        effectiveOpacity);
  }`;
  const colorDemo = Shader.create("colorDemo", cVS, cFS);
  console.log(colorDemo, Shader.find("colorDemo"));

  // rootEntity.transform.scale(new Vector3(4, 4, 4));
  rootEntity.transform.scale(0.5, 0.5, 0.5);

  // const objHref = "/dev-static/obj/windmill/windmill.obj";
  // const objHref = "/dev-static/obj/book/book.obj";
  // const objHref = "/dev-static/obj/cube/cube.obj";
  // const objHref = "/dev-static/obj/plane/plane.obj";
  // const objHref = "/dev-static/obj/chair/chair.obj";
  // const objHref = "/dev-static/obj/Squirtle/Squirtle.obj";
  // const objHref = "/dev-static/obj/pikaqiu/pikaqiu.obj";
  // const objHref = "/dev-static/obj/snowmobile/Snowmobile.obj";
  // rootEntity.transform.scale(0.02, 0.02, 0.02);
  // const objHref = "/dev-static/obj/cargo/cargo.obj";
  const objHref = "/dev-static/obj/elphant/index.obj";
  // const objHref = "/dev-static/obj/jian/index.obj";
  const textures = {
    defaultWhite: TextureUtil.createTexture(gl, { src: [255, 255, 255, 255] })
  };
  const defaultMaterial = {
    diffuse: [1, 1, 1],
    diffuseMap: textures.defaultWhite,
    ambient: [0, 0, 0],
    specular: [1, 1, 1],
    specularMap: textures.defaultWhite,
    shininess: 400,
    opacity: 1
  };

  function cv3(arr) {
    if (arr && arr.length > 0) {
      return new Vector3(arr[0], arr[1], arr[2]);
    }
    return new Vector3(0, 0, 0);
  }
  const u_lightDirection = new Vector3(-1, 3, 5).normalize();

  new OBJLoader(objHref, (meshes) => {
    // debugger
    meshes.map(({ material, data }) => {
      if (data.color) {
        if (data.position.length === data.color.length) {
          // it's 3. The our helper library assumes 4 so we need
          // to tell it there are only 3.
          data.color = { numComponents: 3, data: data.color };
        }
      } else {
        // there are no vertex colors so just use constant white
        data.color = [1, 1, 1, 1];
      }

      const gem = new Geometry();
      gem.addData(new Data(new Float32Array(data.position), [new DataElement("a_position", 3)]), "pos");
      if (data.normal.length) {
        gem.addData(
          new Data(new Float32Array(data.normal), [new DataElement("a_normal", 3, gl.FLOAT, false, 0, 0)]),
          "normal"
        );
      }

      if (data.texcoord.length) {
        gem.addData(
          new Data(new Float32Array(data.texcoord), [new DataElement("a_texcoord", 2, gl.FLOAT, false, 0, 0)]),
          "texcoord"
        );
      }

      if (data.indices.length) {
        gem.addData(new Data(new Uint16Array(data.indices)), "ind");
      }

      material = {
        ...defaultMaterial,
        ...material,
        diffuseMap: TextureUtil.createTexture(gl, {
          src: material ? Common.relativePath(objHref, material.mapDiffuse.filename) : [1, 1, 1, 1],
          color: [1, 1, 1, 1],
          flipY: true
        }),
        specularMap: TextureUtil.createTexture(gl, {
          src: material ? Common.relativePath(objHref, material.mapSpecular.filename) : [1, 1, 1, 1],
          color: [1, 1, 1, 1],
          flipY: true
        }),
        specular: [3, 2, 1],
        shininess: 25
      };
      const matl = new ShaderMaterial("", Shader.find("colorDemo"));
      const shaderData = matl.shaderData;
      shaderData.setVector3("u_lightDirection", u_lightDirection);
      shaderData.setVector3("u_viewWorldPosition", new Vector3(0, 0, 200.0));
      shaderData.setVector3("diffuse", cv3(material.diffuse));
      shaderData.setVector3("ambient", cv3(material.ambient));
      shaderData.setVector3("emissive", cv3(material.emissive));
      shaderData.setVector3("specular", cv3(material.specular));
      shaderData.setVector3("u_ambientLight", new Vector3(0.2, 0.2, 0.2));
      shaderData.setTexture("diffuseMap", { _glTexture: material.diffuseMap, _target: gl.TEXTURE_2D });
      shaderData.setTexture("specularMap", { _glTexture: material.specularMap, _target: gl.TEXTURE_2D });
      shaderData.setFloat("shininess", material.shininess);
      shaderData.setFloat("opacity", material.opacity);
      // rootEntity.addChildNode(new Mesh(gem, matl));
      rootEntity.addChildNode(new WireframeMesh(new Mesh(gem, matl)));
    });
  });

  gui.add(options, "translateX").onChange((v) => {
    rootEntity.transform.setPositionX(v);
  });
  gui.add(options, "translateY").onChange((v) => {
    rootEntity.transform.setPositionY(v);
  });
  gui.add(options, "translateZ").onChange((v) => {
    rootEntity.transform.setPositionZ(v);
  });
  gui.add(options, "scale", 0.01, 0.5).onChange((v) => {
    rootEntity.transform.setScaling(v, v, v);
  });
  gui.add(options, "rotate", 0, Math.PI * 2).onChange((v) => {
    // rootEntity.transform.setScaling(v, v, v);
    rootEntity.transform.setRotationY(v);
  });
  engine.update = () => {
    // rootEntity.transform.rotate(0, 0.01, 0);
  };
}

main();
